#include "stdafx.h"
#include "SoundPlayer.h"

//Set singleton pointer to 0
SoundPlayer * SoundPlayer::theInstance = 0;

SoundPlayer::SoundPlayer() {
	SoundPlayer::initialize();
	m_promptMap = new std::map<int, FMOD::MusicPrompt*>();
	m_timePassed = 0;
	m_queuedList = new std::list<QueuedSound*>();
}

SoundPlayer::~SoundPlayer() {
	m_lastTimeDelta = 1/60;
	delete m_promptMap;
	m_promptMap = NULL;
	delete m_musicSystem;
	m_musicSystem = NULL;
	delete m_queuedList;
	m_queuedList = NULL;

	delete m_system;
	m_system = NULL;
}

SoundPlayer *SoundPlayer::getInstance() {
  if (!theInstance) {
	 theInstance = new SoundPlayer();
  }
  return theInstance;
}

void SoundPlayer::destroyInstance() {
	if(theInstance) {
		delete theInstance;
		theInstance = NULL;
	}
}

void SoundPlayer::activateCue(unsigned int cueId) {
	FMOD::MusicPrompt    *prompt = 0;

	//If map contains cue id, get it
	if(m_promptMap->find(cueId) != m_promptMap->end()) {
		prompt = (*m_promptMap)[cueId];
	}
	
	//If cue not found in map, create it.
	if(!prompt) {
		m_result = m_musicSystem->prepareCue(cueId,    &prompt);
		errcheck(m_result);
	}
	//Start cue
	m_result = prompt->begin();
	errcheck(m_result);
	
}

void SoundPlayer::changeMusicParameter(unsigned int paramId, float paramValue) {
	m_result = m_musicSystem->setParameterValue(paramId, paramValue);
	errcheck(m_result);
}

void SoundPlayer::deactivateCue(unsigned int cueId) {
	FMOD::MusicPrompt    *prompt = 0;

	//If map contains cue id, get it
	if(m_promptMap->find(cueId) != m_promptMap->end()) {
		prompt = (*m_promptMap)[cueId];
	}

	//If cue not found in map, create it.
	if(!prompt) {
		m_result = m_musicSystem->prepareCue(cueId,    &prompt);
		errcheck(m_result);
	}
	
	//Stop cue
	m_result = prompt->end();
  errcheck(m_result);  
}

void SoundPlayer::resetMusic() {
	m_musicSystem->reset();
}

void SoundPlayer::update(float deltaTime) {
	//Update music system
	m_result = m_system->update();
	errcheck(m_result);
	//Calculate total time passed in song
	m_timePassed += deltaTime; 
	m_lastTimeDelta = deltaTime;
	//Iterate through queued sounds
	std::list<QueuedSound*>::iterator i;
	for(i=m_queuedList->begin(); i != m_queuedList->end();)  {
		//If the sound is on beat, play it, and then erase it
		bool onBeat = this->isOnBeat((*i)->bpm);
		if(onBeat) {
			(*i)->sound->play();
		
			m_queuedList->erase(i++);

		} else {			
			++i;
		}
	}
}

int SoundPlayer::initialize() {
	//Create event system
	m_result = FMOD::EventSystem_Create(&m_system);
	errcheck(m_result);

	//Check version
	m_result = m_system->getVersion(&m_version);
  errcheck(m_result);

  if (m_version < FMOD_VERSION) {
  	printf("Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", m_version, FMOD_VERSION);
  	return 0;
  }

	//Init system
	m_result = m_system->init(256, FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED, 0);
	errcheck(m_result);

	//Get project file
	m_result = m_system->setMediaPath("media/");
	errcheck(m_result);

	m_result = m_system->load("fmodproj.fev", 0, 0);
	errcheck(m_result);
	//Get music system
	m_result = m_system->getMusicSystem(&m_musicSystem);
	errcheck(m_result);

	return 0;
}

void SoundPlayer::activateEvent(unsigned int eventId) {
	FMOD::Event    *evt = 0;
	m_result = m_system->getEventBySystemID(eventId, FMOD_EVENT_DEFAULT, &evt);
	errcheck(m_result);
	m_result = evt->start();
	errcheck(m_result);
}

void SoundPlayer::activateEvent3d(unsigned int eventId, float x, float y, float z) {
	FMOD::Event    *evt = 0;
	FMOD_VECTOR pos;
	FMOD_VECTOR vel = {0,0,0};

	pos.x = x;
	pos.y = y;
	pos.z = z;

	m_result = m_system->getEventBySystemID(eventId, FMOD_EVENT_DEFAULT, &evt);
	errcheck(m_result);
	m_result = evt->set3DAttributes(&pos, &vel);
	errcheck(m_result);
	m_result = evt->start();
	errcheck(m_result);
}


void SoundPlayer::resetMusicTimer() {
	m_timePassed = 0;
}

FMOD::Event* SoundPlayer::getEvent(unsigned int eventId) {
	FMOD::Event    *evt = 0;
	m_result = m_system->getEventBySystemID(eventId, FMOD_EVENT_DEFAULT, &evt);
	return evt;
}

void SoundPlayer::errcheck(FMOD_RESULT result) {
	if (result != FMOD_OK) {
  	printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
    exit(-1);
  }
}

void SoundPlayer::update3dListener(float px, float py, float pz, float vx, float vy,float vz,float fx,float fy,float fz,float ux,float uy,float uz ) {
  FMOD_VECTOR forward = { fx, fy, fz };
  FMOD_VECTOR up		= { ux, uy, uz };
  FMOD_VECTOR vel		= { vx, vy, vz };
	FMOD_VECTOR pos		= { px, py, pz };

	m_result = m_system->set3DListenerAttributes(0, &pos, &vel, &forward, &up);
	errcheck(m_result);
}

bool SoundPlayer::isOnBeat(float bpm) {
	//Calculate beats per second
	float beatsPerSecond = bpm/60;
	//Calculate the beat number of the current/previous beat as well as the next beat.
	unsigned int prevBeatNumber = static_cast<unsigned int>(beatsPerSecond*m_timePassed);
	unsigned int nextBeatNumber	= prevBeatNumber + 1;
	float prevBeatTime = prevBeatNumber/beatsPerSecond;
	float nextBeatTime = nextBeatNumber/beatsPerSecond;
	//if time is close enough to any of these to beats, we can say we are on the beat.
	return (abs(m_timePassed-prevBeatTime) < m_lastTimeDelta) || (abs(m_timePassed-nextBeatTime) < m_lastTimeDelta);
}

void SoundPlayer::queueToNextBeat(Sound* sound, float bpm) {
	QueuedSound* qs = new QueuedSound();
	qs->bpm = bpm;
	qs->sound = sound;
	m_queuedList->push_back(qs);
}